#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
minerU PDF 解析器 - 完整功能版本
================================

这是一个功能完整的 minerU PDF 解析程序，支持：
- 自定义 API 地址
- 多种解析方法（auto、ocr、txt）
- 自定义输出目录和文件格式
- 完整的错误处理和日志记录
- 结果保存和格式化
- 批量处理功能
"""

# =============================================================================
# ⚠️  重要提示：请及时更换为您的 API 服务地址  ⚠️
# =============================================================================
# 
# 在使用本程序之前，请务必修改以下配置：
# 1. API_BASE_URL: 更换为您的 minerU API 服务地址
# 2. DEFAULT_PDF_FILE: 指定要解析的 PDF 文件路径
# 3. DEFAULT_OUTPUT_DIR: 设置默认输出目录
# 4. 其他可选配置项
#
# 当前使用的是示例 API 地址，请根据实际情况修改！
# =============================================================================

# =============================================================================
# 用户配置区域 - 请根据您的需求修改以下设置
# =============================================================================

# API 配置
API_BASE_URL = "https://d08041343-mineru070310-318-27tzme3q-8000.550c.cloud"  # ⚠️ 请更换为您的 API 地址
API_TIMEOUT = 300  # API 请求超时时间（秒）

# 文件配置
DEFAULT_PDF_FILE = "minerU论文.pdf"  # 默认要解析的 PDF 文件路径
DEFAULT_OUTPUT_DIR = "output"    # 默认输出目录

# 解析配置
DEFAULT_PARSE_METHOD = "auto"    # 默认解析方法：auto, ocr, txt
DEFAULT_LOG_LEVEL = "INFO"       # 默认日志级别：DEBUG, INFO, WARNING, ERROR
DEFAULT_BACKEND = "pipeline"     # 默认后端：pipeline, vlm-transformers, vlm-sglang-engine, vlm-sglang-client
DEFAULT_LANGUAGE = "ch"          # 默认语言：ch, en, korean, japan, chinese_cht, ta, te, ka

# 功能配置
FORMULA_ENABLE = True            # 是否启用公式解析
TABLE_ENABLE = True              # 是否启用表格解析
DRAW_LAYOUT_BBOX = True          # 是否绘制布局边界框
DRAW_SPAN_BBOX = True            # 是否绘制跨度边界框

# 输出配置
SAVE_RAW_RESPONSE = True         # 是否保存原始 API 响应
CREATE_SUMMARY_REPORT = True     # 是否创建摘要报告
DOWNLOAD_IMAGES = True           # 是否下载图片文件
DUMP_ORIGINAL_PDF = True         # 是否保存原始 PDF
DUMP_CONTENT_LIST = True         # 是否保存内容列表
DUMP_MIDDLE_JSON = True          # 是否保存中间 JSON
DUMP_MODEL_OUTPUT = True         # 是否保存模型输出

# =============================================================================
# 配置说明
# =============================================================================
"""
API_BASE_URL: 
    - 这是您的 minerU API 服务的基础地址
    - 格式：https://your-api-server.com 或 http://localhost:8000
    - 请确保地址正确且服务正在运行

DEFAULT_PDF_FILE:
    - 指定要解析的 PDF 文件路径
    - 可以是相对路径或绝对路径
    - 确保文件存在且有读取权限

DEFAULT_OUTPUT_DIR:
    - 解析结果的输出目录
    - 程序会自动创建此目录
    - 建议使用有意义的目录名

DEFAULT_PARSE_METHOD:
    - auto: 自动选择最适合的解析方法（推荐）
    - ocr: 使用光学字符识别（适用于扫描版 PDF）
    - txt: 直接提取文本（适用于纯文本 PDF）

API_TIMEOUT:
    - API 请求的超时时间
    - 大文件或慢网络可能需要增加此值
    - 单位：秒

DEFAULT_LOG_LEVEL:
    - DEBUG: 显示所有详细信息（调试用）
    - INFO: 显示一般信息（推荐）
    - WARNING: 只显示警告和错误
    - ERROR: 只显示错误信息

SAVE_RAW_RESPONSE:
    - True: 保存完整的 API 响应（用于调试）
    - False: 不保存原始响应（节省空间）

CREATE_SUMMARY_REPORT:
    - True: 创建人类可读的摘要报告
    - False: 不创建摘要报告

DOWNLOAD_IMAGES:
    - True: 下载并保存图片文件到 images 目录
    - False: 不下载图片文件（节省空间和网络带宽）

DUMP_ORIGINAL_PDF:
    - True: 保存原始 PDF 文件副本
    - False: 不保存原始 PDF（节省空间）

DUMP_CONTENT_LIST:
    - True: 保存结构化内容列表
    - False: 不保存内容列表

DUMP_MIDDLE_JSON:
    - True: 保存中间处理 JSON 文件
    - False: 不保存中间 JSON

DUMP_MODEL_OUTPUT:
    - True: 保存模型原始输出
    - False: 不保存模型输出

DEFAULT_BACKEND:
    - pipeline: 通用后端，支持多种文档类型
    - vlm-transformers: 基于 Transformers 的 VLM 后端
    - vlm-sglang-engine: 快速 VLM 引擎后端
    - vlm-sglang-client: 快速 VLM 客户端后端

DEFAULT_LANGUAGE:
    - ch: 中文（默认）
    - en: 英文
    - korean: 韩文
    - japan: 日文
    - chinese_cht: 繁体中文
    - ta: 泰米尔语
    - te: 泰卢固语
    - ka: 格鲁吉亚语

FORMULA_ENABLE:
    - True: 启用数学公式识别
    - False: 禁用公式识别

TABLE_ENABLE:
    - True: 启用表格识别
    - False: 禁用表格识别

DRAW_LAYOUT_BBOX:
    - True: 生成带布局边界框的 PDF
    - False: 不生成布局边界框

DRAW_SPAN_BBOX:
    - True: 生成带跨度边界框的 PDF
    - False: 不生成跨度边界框
"""

import requests
import os
import json
import time
import logging
from pathlib import Path
from datetime import datetime
from typing import Optional, Dict, Any, List, Union
import argparse


class MinerUPDFParser:
    """
    minerU PDF 解析器主类
    
    提供完整的 PDF 解析功能，支持自定义配置和批量处理
    """
    
    def __init__(self, 
                 api_base_url: str = None,
                 timeout: int = None,
                 enable_logging: bool = True,
                 log_level: str = None):
        """
        初始化 PDF 解析器
        
        Args:
            api_base_url: API 基础地址，如果为 None 则使用配置文件中的设置
            timeout: 请求超时时间（秒），如果为 None 则使用配置文件中的设置
            enable_logging: 是否启用日志记录
            log_level: 日志级别（DEBUG, INFO, WARNING, ERROR），如果为 None 则使用配置文件中的设置
        """
        # 使用配置文件中的默认值
        self.api_base_url = (api_base_url or API_BASE_URL).rstrip('/')
        self.api_endpoint = f"{self.api_base_url}/pdf_parse"
        self.timeout = timeout or API_TIMEOUT
        self.session = requests.Session()
        
        # 设置日志
        if enable_logging:
            self._setup_logging(log_level or DEFAULT_LOG_LEVEL)
        else:
            self.logger = None
    
    def _setup_logging(self, log_level: str):
        """设置日志记录"""
        self.logger = logging.getLogger('MinerUPDFParser')
        self.logger.setLevel(getattr(logging, log_level.upper()))
        
        # 创建控制台处理器
        console_handler = logging.StreamHandler()
        console_handler.setLevel(getattr(logging, log_level.upper()))
        
        # 创建文件处理器
        file_handler = logging.FileHandler('minerU_parser.log', encoding='utf-8')
        file_handler.setLevel(getattr(logging, log_level.upper()))
        
        # 设置格式
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        console_handler.setFormatter(formatter)
        file_handler.setFormatter(formatter)
        
        # 添加处理器
        self.logger.addHandler(console_handler)
        self.logger.addHandler(file_handler)
    
    def check_api_connection(self) -> bool:
        """
        检查 API 连接状态
        
        Returns:
            bool: 连接是否正常
        """
        try:
            if self.logger:
                self.logger.info("检查 API 连接...")
            
            # 尝试访问 API 文档
            response = self.session.get(f"{self.api_base_url}/docs", timeout=10)
            
            if response.status_code == 200:
                if self.logger:
                    self.logger.info("✓ API 连接正常")
                return True
            else:
                if self.logger:
                    self.logger.error(f"✗ API 连接异常，状态码: {response.status_code}")
                return False
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"✗ API 连接失败: {e}")
            return False
    
    def parse_pdf(self,
                  pdf_file_path: str = None,
                  parse_method: str = None,
                  backend: str = None,
                  language: str = None,
                  formula_enable: bool = None,
                  table_enable: bool = None,
                  draw_layout_bbox: bool = None,
                  draw_span_bbox: bool = None,
                  model_json_path: Optional[str] = None,
                  is_json_md_dump: bool = True,
                  output_dir: str = None,
                  save_raw_response: bool = None,
                  create_summary: bool = None,
                  download_images: bool = None,
                  dump_original_pdf: bool = None,
                  dump_content_list: bool = None,
                  dump_middle_json: bool = None,
                  dump_model_output: bool = None,
                  start_page_id: int = 0,
                  end_page_id: Optional[int] = None) -> Dict[str, Any]:
        """
        解析单个 PDF 文件
        
        Args:
            pdf_file_path: PDF 文件路径，如果为 None 则使用配置文件中的设置
            parse_method: 解析方法，可选值：auto, ocr, txt，如果为 None 则使用配置文件中的设置
            backend: 后端类型，可选值：pipeline, vlm-transformers, vlm-sglang-engine, vlm-sglang-client
            language: 文档语言，可选值：ch, en, korean, japan, chinese_cht, ta, te, ka
            formula_enable: 是否启用公式解析
            table_enable: 是否启用表格解析
            draw_layout_bbox: 是否绘制布局边界框
            draw_span_bbox: 是否绘制跨度边界框
            model_json_path: 模型文件路径（可选）
            is_json_md_dump: 是否输出 JSON 和 MD 文件
            output_dir: 输出目录，如果为 None 则使用配置文件中的设置
            save_raw_response: 是否保存原始响应，如果为 None 则使用配置文件中的设置
            create_summary: 是否创建摘要报告，如果为 None 则使用配置文件中的设置
            download_images: 是否下载图片文件，如果为 None 则使用配置文件中的设置
            dump_original_pdf: 是否保存原始 PDF
            dump_content_list: 是否保存内容列表
            dump_middle_json: 是否保存中间 JSON
            dump_model_output: 是否保存模型输出
            start_page_id: 开始页面 ID（从 0 开始）
            end_page_id: 结束页面 ID（None 表示解析到最后）
            
        Returns:
            Dict[str, Any]: 解析结果字典
        """
        # 使用配置文件中的默认值
        pdf_file_path = pdf_file_path or DEFAULT_PDF_FILE
        parse_method = parse_method or DEFAULT_PARSE_METHOD
        backend = backend or DEFAULT_BACKEND
        language = language or DEFAULT_LANGUAGE
        formula_enable = formula_enable if formula_enable is not None else FORMULA_ENABLE
        table_enable = table_enable if table_enable is not None else TABLE_ENABLE
        draw_layout_bbox = draw_layout_bbox if draw_layout_bbox is not None else DRAW_LAYOUT_BBOX
        draw_span_bbox = draw_span_bbox if draw_span_bbox is not None else DRAW_SPAN_BBOX
        output_dir = output_dir or DEFAULT_OUTPUT_DIR
        save_raw_response = save_raw_response if save_raw_response is not None else SAVE_RAW_RESPONSE
        create_summary = create_summary if create_summary is not None else CREATE_SUMMARY_REPORT
        download_images = download_images if download_images is not None else DOWNLOAD_IMAGES
        dump_original_pdf = dump_original_pdf if dump_original_pdf is not None else DUMP_ORIGINAL_PDF
        dump_content_list = dump_content_list if dump_content_list is not None else DUMP_CONTENT_LIST
        dump_middle_json = dump_middle_json if dump_middle_json is not None else DUMP_MIDDLE_JSON
        dump_model_output = dump_model_output if dump_model_output is not None else DUMP_MODEL_OUTPUT
        
        # 检查文件是否存在
        if not os.path.exists(pdf_file_path):
            error_msg = f"PDF 文件不存在: {pdf_file_path}"
            if self.logger:
                self.logger.error(error_msg)
            raise FileNotFoundError(error_msg)
        
        # 获取文件信息
        file_size = os.path.getsize(pdf_file_path) / 1024 / 1024  # MB
        if self.logger:
            self.logger.info(f"开始解析 PDF: {pdf_file_path} ({file_size:.2f} MB)")
        
        # 创建输出目录
        output_path = Path(output_dir)
        output_path.mkdir(parents=True, exist_ok=True)
        
        # 准备请求参数 - 根据 API 文档调整
        params = {
            'parse_method': parse_method,
            'is_json_md_dump': is_json_md_dump,
            'output_dir': str(output_path)
        }
        
        # 添加模型文件路径（如果提供）
        if model_json_path:
            params['model_json_path'] = model_json_path
        
        # 准备文件
        with open(pdf_file_path, 'rb') as pdf_file:
            files = {
                'pdf_file': (os.path.basename(pdf_file_path), pdf_file, 'application/pdf')
            }
            
            try:
                if self.logger:
                    self.logger.info(f"发送解析请求，方法: {parse_method}")
                
                # 发送请求
                start_time = time.time()
                response = self.session.post(
                    self.api_endpoint, 
                    params=params, 
                    files=files, 
                    timeout=self.timeout
                )
                end_time = time.time()
                
                if self.logger:
                    self.logger.info(f"请求完成，耗时: {end_time - start_time:.2f} 秒")
                
                # 处理响应
                if response.status_code == 200:
                    if self.logger:
                        self.logger.info("✓ PDF 解析成功")
                    
                    result = response.json()
                    
                    # 保存结果
                    self._save_parse_results(
                        result, output_path, pdf_file_path, file_size,
                        save_raw_response, create_summary, download_images,
                        dump_original_pdf, dump_content_list, dump_middle_json, dump_model_output
                    )
                    
                    return result
                else:
                    error_msg = f"PDF 解析失败，状态码: {response.status_code}, 响应: {response.text}"
                    if self.logger:
                        self.logger.error(error_msg)
                    raise Exception(error_msg)
                    
            except requests.exceptions.Timeout:
                error_msg = f"请求超时（{self.timeout} 秒）"
                if self.logger:
                    self.logger.error(error_msg)
                raise Exception(error_msg)
            except requests.exceptions.RequestException as e:
                error_msg = f"请求失败: {e}"
                if self.logger:
                    self.logger.error(error_msg)
                raise Exception(error_msg)
            except Exception as e:
                error_msg = f"解析过程中发生错误: {e}"
                if self.logger:
                    self.logger.error(error_msg)
                raise Exception(error_msg)
    
    def _save_parse_results(self, 
                           result: Dict[str, Any], 
                           output_path: Path,
                           pdf_file_path: str,
                           file_size: float,
                           save_raw_response: bool,
                           create_summary: bool,
                           download_images: bool,
                           dump_original_pdf: bool,
                           dump_content_list: bool,
                           dump_middle_json: bool,
                           dump_model_output: bool):
        """
        保存解析结果到文件
        
        Args:
            result: 解析结果
            output_path: 输出路径
            pdf_file_path: 原始 PDF 文件路径
            file_size: 文件大小（MB）
            save_raw_response: 是否保存原始响应
            create_summary: 是否创建摘要
        """
        try:
            # 保存原始 JSON 响应
            if save_raw_response:
                raw_file = output_path / "raw_response.json"
                with open(raw_file, 'w', encoding='utf-8') as f:
                    json.dump(result, f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 原始响应已保存: {raw_file}")
            
            # 保存 Markdown 内容
            if 'md_content' in result:
                md_file = output_path / "parsed_content.md"
                with open(md_file, 'w', encoding='utf-8') as f:
                    f.write(result['md_content'])
                if self.logger:
                    self.logger.info(f"✓ Markdown 内容已保存: {md_file}")
            
            # 保存内容列表
            if 'content_list' in result:
                content_file = output_path / "content_list.json"
                with open(content_file, 'w', encoding='utf-8') as f:
                    json.dump(result['content_list'], f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 内容列表已保存: {content_file}")
            
            # 保存布局信息
            if 'layout' in result:
                layout_file = output_path / "layout_info.json"
                with open(layout_file, 'w', encoding='utf-8') as f:
                    json.dump(result['layout'], f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 布局信息已保存: {layout_file}")
            
            # 保存解析信息
            if 'info' in result:
                info_file = output_path / "parse_info.json"
                with open(info_file, 'w', encoding='utf-8') as f:
                    json.dump(result['info'], f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 解析信息已保存: {info_file}")
            
            # 保存原始 PDF 副本
            if dump_original_pdf:
                self._save_original_pdf(pdf_file_path, output_path)
            
            # 保存内容列表
            if dump_content_list and 'content_list' in result:
                content_file = output_path / "content_list.json"
                with open(content_file, 'w', encoding='utf-8') as f:
                    json.dump(result['content_list'], f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 内容列表已保存: {content_file}")
            
            # 保存中间 JSON
            if dump_middle_json and 'middle_json' in result:
                middle_file = output_path / "middle.json"
                with open(middle_file, 'w', encoding='utf-8') as f:
                    json.dump(result['middle_json'], f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 中间 JSON 已保存: {middle_file}")
            
            # 保存模型输出
            if dump_model_output and 'model_output' in result:
                model_file = output_path / "model_output.json"
                with open(model_file, 'w', encoding='utf-8') as f:
                    json.dump(result['model_output'], f, ensure_ascii=False, indent=2)
                if self.logger:
                    self.logger.info(f"✓ 模型输出已保存: {model_file}")
            
            # 下载和保存图片
            if download_images:
                self._download_images(result, output_path)
            
            # 创建摘要报告
            if create_summary:
                self._create_summary_report(result, output_path, pdf_file_path, file_size)
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"保存结果时发生错误: {e}")
    
    def _download_images(self, result: Dict[str, Any], output_path: Path):
        """
        下载和保存图片文件
        
        Args:
            result: 解析结果
            output_path: 输出路径
        """
        try:
            # 创建图片目录
            images_dir = output_path / "images"
            images_dir.mkdir(exist_ok=True)
            
            # 收集所有图片路径
            image_paths = set()
            
            # 从不同字段中提取图片路径
            if 'layout' in result:
                for page in result['layout']:
                    if 'images' in page:
                        for img in page['images']:
                            if 'image_path' in img:
                                image_paths.add(img['image_path'])
            
            if 'content_list' in result:
                for item in result['content_list']:
                    if isinstance(item, dict) and 'image_path' in item:
                        image_paths.add(item['image_path'])
            
            # 下载图片
            downloaded_count = 0
            for image_path in image_paths:
                try:
                    # 构建完整的图片 URL
                    image_url = f"{self.api_base_url}/images/{image_path}"
                    
                    # 下载图片
                    response = self.session.get(image_url, timeout=30)
                    if response.status_code == 200:
                        # 保存图片
                        image_file = images_dir / image_path
                        with open(image_file, 'wb') as f:
                            f.write(response.content)
                        downloaded_count += 1
                        if self.logger:
                            self.logger.info(f"✓ 图片已下载: {image_path}")
                    else:
                        if self.logger:
                            self.logger.warning(f"✗ 图片下载失败: {image_path}, 状态码: {response.status_code}")
                            
                except Exception as e:
                    if self.logger:
                        self.logger.warning(f"✗ 图片下载出错: {image_path}, 错误: {e}")
            
            if self.logger:
                self.logger.info(f"✓ 图片下载完成: {downloaded_count}/{len(image_paths)} 个图片")
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"下载图片时发生错误: {e}")
    
    def _save_original_pdf(self, pdf_file_path: str, output_path: Path):
         """
         保存原始 PDF 文件副本
         
         Args:
             pdf_file_path: 原始 PDF 文件路径
             output_path: 输出路径
         """
         try:
             import shutil
             pdf_name = Path(pdf_file_path).name
             pdf_copy_path = output_path / f"original_{pdf_name}"
             shutil.copy2(pdf_file_path, pdf_copy_path)
             if self.logger:
                 self.logger.info(f"✓ 原始 PDF 已保存: {pdf_copy_path}")
         except Exception as e:
             if self.logger:
                 self.logger.error(f"保存原始 PDF 时发生错误: {e}")
     
    def _create_summary_report(self,  
                              result: Dict[str, Any], 
                              output_path: Path,
                              pdf_file_path: str,
                              file_size: float):
        """
        创建摘要报告
        
        Args:
            result: 解析结果
            output_path: 输出路径
            pdf_file_path: 原始 PDF 文件路径
            file_size: 文件大小（MB）
        """
        try:
            summary_file = output_path / "summary.txt"
            with open(summary_file, 'w', encoding='utf-8') as f:
                f.write("=" * 60 + "\n")
                f.write("minerU PDF 解析结果摘要\n")
                f.write("=" * 60 + "\n")
                f.write(f"解析时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"源文件: {pdf_file_path}\n")
                f.write(f"文件大小: {file_size:.2f} MB\n")
                f.write(f"输出目录: {output_path}\n")
                f.write("-" * 60 + "\n")
                
                # 解析信息
                if 'info' in result:
                    f.write("解析信息:\n")
                    for key, value in result['info'].items():
                        f.write(f"  {key}: {value}\n")
                    f.write("\n")
                
                # 内容统计
                if 'content_list' in result:
                    f.write("内容统计:\n")
                    f.write(f"  总内容项数: {len(result['content_list'])}\n")
                    
                    # 统计不同类型的内容
                    content_types = {}
                    for item in result['content_list']:
                        content_type = item.get('type', 'unknown')
                        content_types[content_type] = content_types.get(content_type, 0) + 1
                    
                    for content_type, count in content_types.items():
                        f.write(f"  {content_type}: {count}\n")
                    f.write("\n")
                
                # 文件列表
                f.write("生成的文件:\n")
                for file_path in output_path.glob("*"):
                    if file_path.is_file():
                        f.write(f"  {file_path.name}\n")
                
                f.write("=" * 60 + "\n")
            
            if self.logger:
                self.logger.info(f"✓ 摘要报告已保存: {summary_file}")
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"创建摘要报告时发生错误: {e}")
    
    def batch_parse(self, 
                    pdf_files: List[str],
                    parse_method: str = "auto",
                    output_base_dir: str = "batch_output",
                    **kwargs) -> List[Dict[str, Any]]:
        """
        批量解析多个 PDF 文件
        
        Args:
            pdf_files: PDF 文件路径列表
            parse_method: 解析方法
            output_base_dir: 基础输出目录
            **kwargs: 其他参数传递给 parse_pdf 方法
            
        Returns:
            List[Dict[str, Any]]: 所有解析结果列表
        """
        if self.logger:
            self.logger.info(f"开始批量解析 {len(pdf_files)} 个文件")
        
        results = []
        success_count = 0
        error_count = 0
        
        for i, pdf_file in enumerate(pdf_files, 1):
            try:
                if self.logger:
                    self.logger.info(f"处理文件 {i}/{len(pdf_files)}: {pdf_file}")
                
                # 为每个文件创建独立的输出目录
                file_name = Path(pdf_file).stem
                output_dir = os.path.join(output_base_dir, file_name)
                
                # 解析文件
                result = self.parse_pdf(
                    pdf_file_path=pdf_file,
                    parse_method=parse_method,
                    output_dir=output_dir,
                    **kwargs
                )
                
                results.append({
                    'file': pdf_file,
                    'status': 'success',
                    'result': result
                })
                success_count += 1
                
            except Exception as e:
                if self.logger:
                    self.logger.error(f"解析文件失败 {pdf_file}: {e}")
                
                results.append({
                    'file': pdf_file,
                    'status': 'error',
                    'error': str(e)
                })
                error_count += 1
        
        # 创建批量处理摘要
        self._create_batch_summary(results, output_base_dir, success_count, error_count)
        
        if self.logger:
            self.logger.info(f"批量解析完成: 成功 {success_count} 个，失败 {error_count} 个")
        
        return results
    
    def _create_batch_summary(self, 
                             results: List[Dict[str, Any]], 
                             output_base_dir: str,
                             success_count: int,
                             error_count: int):
        """
        创建批量处理摘要
        
        Args:
            results: 解析结果列表
            output_base_dir: 输出基础目录
            success_count: 成功数量
            error_count: 失败数量
        """
        try:
            summary_file = os.path.join(output_base_dir, "batch_summary.txt")
            with open(summary_file, 'w', encoding='utf-8') as f:
                f.write("=" * 60 + "\n")
                f.write("minerU PDF 批量解析摘要\n")
                f.write("=" * 60 + "\n")
                f.write(f"处理时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"总文件数: {len(results)}\n")
                f.write(f"成功数量: {success_count}\n")
                f.write(f"失败数量: {error_count}\n")
                f.write(f"成功率: {success_count/len(results)*100:.1f}%\n")
                f.write("-" * 60 + "\n")
                
                # 成功文件列表
                f.write("成功解析的文件:\n")
                for result in results:
                    if result['status'] == 'success':
                        f.write(f"  ✓ {result['file']}\n")
                
                f.write("\n")
                
                # 失败文件列表
                if error_count > 0:
                    f.write("解析失败的文件:\n")
                    for result in results:
                        if result['status'] == 'error':
                            f.write(f"  ✗ {result['file']}: {result['error']}\n")
                
                f.write("=" * 60 + "\n")
            
            if self.logger:
                self.logger.info(f"✓ 批量处理摘要已保存: {summary_file}")
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"创建批量摘要时发生错误: {e}")


def main():
    """
    主函数 - 程序入口点
    """
    # 创建命令行参数解析器
    parser = argparse.ArgumentParser(
        description="minerU PDF 解析器 - 完整功能版本",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=f"""
使用示例:
  # 解析默认文件（使用配置文件中的设置）
  python minerU_complete_parser.py
  
  # 解析指定文件
  python minerU_complete_parser.py -f {DEFAULT_PDF_FILE}
  
  # 使用 OCR 方法解析
  python minerU_complete_parser.py -f {DEFAULT_PDF_FILE} -m ocr
  
  # 自定义输出目录
  python minerU_complete_parser.py -f {DEFAULT_PDF_FILE} -o my_output
  
  # 批量解析
  python minerU_complete_parser.py -b file1.pdf file2.pdf file3.pdf
  
  # 自定义 API 地址
  python minerU_complete_parser.py -f {DEFAULT_PDF_FILE} --api-url https://your-api-url.com
  
  # 查看所有选项
  python minerU_complete_parser.py --help
        """
    )
    
    # 添加参数
    parser.add_argument('-f', '--file', 
                       help='要解析的 PDF 文件路径')
    parser.add_argument('-b', '--batch', nargs='+',
                       help='批量解析多个 PDF 文件')
    parser.add_argument('-m', '--method', 
                       choices=['auto', 'ocr', 'txt'],
                       default=DEFAULT_PARSE_METHOD,
                       help=f'解析方法 (默认: {DEFAULT_PARSE_METHOD})')
    parser.add_argument('-o', '--output', 
                       default=DEFAULT_OUTPUT_DIR,
                       help=f'输出目录 (默认: {DEFAULT_OUTPUT_DIR})')
    parser.add_argument('--api-url',
                       default=API_BASE_URL,
                       help='API 基础地址')
    parser.add_argument('--timeout', 
                       type=int, default=API_TIMEOUT,
                       help=f'请求超时时间（秒）(默认: {API_TIMEOUT})')
    parser.add_argument('--no-logging', 
                       action='store_true',
                       help='禁用日志记录')
    parser.add_argument('--log-level',
                       choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
                       default=DEFAULT_LOG_LEVEL,
                       help=f'日志级别 (默认: {DEFAULT_LOG_LEVEL})')
    parser.add_argument('--model-path',
                       help='模型文件路径（可选）')
    parser.add_argument('--no-raw-save',
                       action='store_true',
                       help='不保存原始响应')
    parser.add_argument('--no-summary',
                       action='store_true',
                       help='不创建摘要报告')
    parser.add_argument('--no-images',
                       action='store_true',
                       help='不下载图片文件')
    # 注意：以下参数在 API 文档中未明确支持，但保留作为扩展功能
    parser.add_argument('--backend',
                       choices=['pipeline', 'vlm-transformers', 'vlm-sglang-engine', 'vlm-sglang-client'],
                       default=DEFAULT_BACKEND,
                       help=f'后端类型 (默认: {DEFAULT_BACKEND}) - API 文档中未明确支持')
    parser.add_argument('--language',
                       choices=['ch', 'en', 'korean', 'japan', 'chinese_cht', 'ta', 'te', 'ka'],
                       default=DEFAULT_LANGUAGE,
                       help=f'文档语言 (默认: {DEFAULT_LANGUAGE}) - API 文档中未明确支持')
    parser.add_argument('--no-formula',
                       action='store_true',
                       help='禁用公式解析 - API 文档中未明确支持')
    parser.add_argument('--no-table',
                       action='store_true',
                       help='禁用表格解析 - API 文档中未明确支持')
    parser.add_argument('--no-layout-bbox',
                       action='store_true',
                       help='不绘制布局边界框 - API 文档中未明确支持')
    parser.add_argument('--no-span-bbox',
                       action='store_true',
                       help='不绘制跨度边界框 - API 文档中未明确支持')
    parser.add_argument('--no-original-pdf',
                       action='store_true',
                       help='不保存原始 PDF')
    parser.add_argument('--no-content-list',
                       action='store_true',
                       help='不保存内容列表')
    parser.add_argument('--no-middle-json',
                       action='store_true',
                       help='不保存中间 JSON')
    parser.add_argument('--no-model-output',
                       action='store_true',
                       help='不保存模型输出')
    parser.add_argument('--start-page',
                       type=int, default=0,
                       help='开始页面 ID (默认: 0) - API 文档中未明确支持')
    parser.add_argument('--end-page',
                       type=int, default=None,
                       help='结束页面 ID (默认: 解析到最后) - API 文档中未明确支持')
    
    # 解析参数
    args = parser.parse_args()
    
    # 检查参数
    if not args.file and not args.batch:
        print("错误: 必须指定要解析的文件 (-f) 或批量文件 (-b)")
        parser.print_help()
        return
    
    # 创建解析器实例
    parser_instance = MinerUPDFParser(
        api_base_url=args.api_url,
        timeout=args.timeout,
        enable_logging=not args.no_logging,
        log_level=args.log_level
    )
    
    print("=" * 60)
    print("minerU PDF 解析器 - 完整功能版本")
    print("=" * 60)
    print(f"API 地址: {args.api_url}")
    print(f"解析方法: {args.method}")
    print(f"输出目录: {args.output}")
    print(f"超时时间: {args.timeout} 秒")
    print("-" * 60)
    print("⚠️  注意：请确保已修改配置文件中的 API 地址！")
    print("-" * 60)
    
    try:
        # 检查 API 连接
        if not parser_instance.check_api_connection():
            print("✗ API 连接失败，请检查网络连接和 API 地址")
            return
        
        # 执行解析
        if args.batch:
            # 批量解析
            print(f"开始批量解析 {len(args.batch)} 个文件...")
            results = parser_instance.batch_parse(
                 pdf_files=args.batch,
                 parse_method=args.method,
                 backend=args.backend,
                 language=args.language,
                 formula_enable=not args.no_formula,
                 table_enable=not args.no_table,
                 draw_layout_bbox=not args.no_layout_bbox,
                 draw_span_bbox=not args.no_span_bbox,
                 output_base_dir=args.output,
                 model_json_path=args.model_path,
                 save_raw_response=not args.no_raw_save,
                 create_summary=not args.no_summary,
                 download_images=not args.no_images,
                 dump_original_pdf=not args.no_original_pdf,
                 dump_content_list=not args.no_content_list,
                 dump_middle_json=not args.no_middle_json,
                 dump_model_output=not args.no_model_output,
                 start_page_id=args.start_page,
                 end_page_id=args.end_page
             )
            
            # 显示结果
            success_count = sum(1 for r in results if r['status'] == 'success')
            print(f"\n批量解析完成: 成功 {success_count}/{len(results)} 个文件")
            
        else:
            # 单个文件解析
            print(f"开始解析文件: {args.file}")
            result = parser_instance.parse_pdf(
                pdf_file_path=args.file,
                parse_method=args.method,
                backend=args.backend,
                language=args.language,
                formula_enable=not args.no_formula,
                table_enable=not args.no_table,
                draw_layout_bbox=not args.no_layout_bbox,
                draw_span_bbox=not args.no_span_bbox,
                model_json_path=args.model_path,
                output_dir=args.output,
                save_raw_response=not args.no_raw_save,
                create_summary=not args.no_summary,
                download_images=not args.no_images,
                dump_original_pdf=not args.no_original_pdf,
                dump_content_list=not args.no_content_list,
                dump_middle_json=not args.no_middle_json,
                dump_model_output=not args.no_model_output,
                start_page_id=args.start_page,
                end_page_id=args.end_page
            )
            
            print("✓ 文件解析完成")
            print(f"输出目录: {args.output}")
        
        print("\n程序执行完成！")
        
    except KeyboardInterrupt:
        print("\n用户中断程序执行")
    except Exception as e:
        print(f"\n程序执行出错: {e}")


if __name__ == "__main__":
    # 如果没有命令行参数，运行示例
    import sys
    if len(sys.argv) == 1:
        print("=" * 60)
        print("minerU PDF 解析器 - 完整功能版本")
        print("=" * 60)
        print("正在运行示例程序...")
        print(f"使用配置文件中的设置：")
        print(f"  - PDF 文件: {DEFAULT_PDF_FILE}")
        print(f"  - API 地址: {API_BASE_URL}")
        print(f"  - 解析方法: {DEFAULT_PARSE_METHOD}")
        print(f"  - 输出目录: {DEFAULT_OUTPUT_DIR}")
        print()
        print("⚠️  注意：请确保已修改配置文件中的 API 地址！")
        print()
        
        # 创建解析器实例
        parser = MinerUPDFParser()
        
        # 检查 API 连接
        if not parser.check_api_connection():
            print("✗ API 连接失败")
            sys.exit(1)
        
        # 检查示例文件
        pdf_file = DEFAULT_PDF_FILE
        if not os.path.exists(pdf_file):
            print(f"✗ 示例文件不存在: {pdf_file}")
            print(f"请确保 {DEFAULT_PDF_FILE} 文件在当前目录中")
            print("或者修改配置文件中的 DEFAULT_PDF_FILE 设置")
            sys.exit(1)
        
        try:
            # 运行示例解析
            print(f"开始解析示例文件: {pdf_file}")
            result = parser.parse_pdf(
                pdf_file_path=pdf_file,
                parse_method=DEFAULT_PARSE_METHOD,
                backend=DEFAULT_BACKEND,
                language=DEFAULT_LANGUAGE,
                formula_enable=FORMULA_ENABLE,
                table_enable=TABLE_ENABLE,
                draw_layout_bbox=DRAW_LAYOUT_BBOX,
                draw_span_bbox=DRAW_SPAN_BBOX,
                output_dir="example_output",
                save_raw_response=SAVE_RAW_RESPONSE,
                create_summary=CREATE_SUMMARY_REPORT,
                download_images=DOWNLOAD_IMAGES,
                dump_original_pdf=DUMP_ORIGINAL_PDF,
                dump_content_list=DUMP_CONTENT_LIST,
                dump_middle_json=DUMP_MIDDLE_JSON,
                dump_model_output=DUMP_MODEL_OUTPUT
            )
            
            print("✓ 示例解析完成")
            print("输出目录: example_output")
            print("\n您可以使用以下命令查看更多选项:")
            print("python minerU_complete_parser.py --help")
            
        except Exception as e:
            print(f"✗ 示例解析失败: {e}")
            sys.exit(1)
    else:
        main() 